<?php
/**
 * Created by PhpStorm.
 * User: meng
 * Date: 2019-04-17
 * Time: 15:56
 */

namespace wechat\wxpay;


use think\facade\Log;

class WechatCompanypay
{
    public $appid;
    public $appsecret;
    public $mchid;
    public $mchkey;
    public $apiclient_cert;
    public $apiclient_key;

    public function __construct($appid = null, $appsecret = null, $mchid = null, $mchkey = null)
    {
        $this->appid = $appid ? $appid : 'wxe0975b379f675f52';
        $this->appsecret = $appsecret ? $appsecret : '2e6bb444ac0c8553c26d36a38a91d551';
        $this->mchid = $mchid ? $mchid : '1488446202';
        $this->mchkey = $mchkey ? $mchkey : '5f9ba72a53787208ba7a86c35dfdb168';
        //$this->apiclient_cert = dirname(__FILE__) . '/cert/apiclient_cert.pem';
        //$this->apiclient_key = dirname(__FILE__) . '/cert/apiclient_key.pem';
        $this->apiclient_cert = '../public/upload/3540892cc567142d/effd0d9141ec4bf3.pem';
        $this->apiclient_key = '../public/upload/3540892cc567142d/effd0d9144erdf4bf3.pem';
    }

    /**
     * 测试证书是否能够获取到
     * @return false|string
     */
    public function testCert()
    {
        return file_get_contents($this->apiclient_cert);
    }

    /**
     * 调用企业支付
     * @param $osn string 商户生成订单号
     * @param $openid string openid
     * @param $money int 金额
     * @param $desc string 描述
     * @return mixed
     */
    public function enterprisePay($osn, $openid, $money, $desc)
    {
//        return $this->testCert();
        $params = array(
            'partner_trade_no' => $osn,
            'openid' => $openid,
            'check_name' => 'NO_CHECK',
            'amount' => $money, // 总计
            'desc' => $desc,
        );
        $result = $this->payToUser($params);
        return $result;
    }

    /**
     * 企业向个人付款
     * @param $params
     * @return mixed
     */
    public function payToUser($params)
    {
        $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
        //检测必填参数
        if ($params["partner_trade_no"] == null) {
            exit("付款申请接口中，缺少必填参数partner_trade_no！" . "<br>");
        } elseif ($params["openid"] == null) {
            exit("付款申请接口中，缺少必填参数openid！" . "<br>");
        } elseif ($params["check_name"] == null) {
            exit("付款申请接口中，缺少必填参数check_name！" . "<br>");
        } elseif (($params["check_name"] == 'FORCE_CHECK' or $params["check_name"] == 'OPTION_CHECK') && ($params["re_user_name"] == null)) {  //收款用户真实姓名。
            exit("付款申请接口中，缺少必填参数re_user_name！" . "<br>");
        } elseif ($params["amount"] == null) { // 金额
            exit("付款申请接口中，缺少必填参数amount！" . "<br>");
        } elseif ($params["desc"] == null) { // 描述
            exit("付款申请接口中，缺少必填参数desc！" . "<br>");
        }
        $params["mch_appid"] = $this->appid;//公众账号ID
        $params["mchid"] = $this->mchid;//商户号
        $params["nonce_str"] = $this->createNoncestr();//随机字符串
        $params['spbill_create_ip'] = '140.143.23.155';//获取IP
        $params["sign"] = $this->getSign($params);//签名
        //dump($params);die;
        $xml = $this->arrayToXml($params);
        return $this->postXmlSSLCurl($xml, $url, false, $this->apiclient_cert, $this->apiclient_key);
    }

    /**
     * @param $Obj
     * @return string 签名
     */
    public function getSign($Obj)
    {
        foreach ($Obj as $k => $v) {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //签名步骤二：在string后加入KEY
        $String = $String . "&key=" . $this->mchkey;
        //签名步骤三：MD5加密
        $String = md5($String);
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        return $result_;
    }

    /**
     * 作用：格式化参数，签名过程需要使用
     * @param $paraMap
     * @param $urlencode boolean
     * @return bool|string
     */
    public function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

    /**
     * 作用：产生随机字符串，不长于32位
     * @param int $length 长度
     * @return string
     */
    public function createNoncestr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * array转xml
     * @param $arr
     * @return string
     */
    public function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 作用：使用证书，以post方式提交xml到对应的接口url
     * @param $xml string
     * @param $url string
     * @param $second
     * @param $cert
     * @param $key
     * @return bool
     */
    function postXmlSSLCurl($xml, $url, $second, $cert, $key)
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second ? $second : 500);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //设置证书
        //使用证书：cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $cert);
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $key);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            return $this->xmlToArray($data);
        } else {
            $error = curl_errno($ch);
            Log::error('curl出错，错误码' . $error);
            curl_close($ch);
            return false;
        }
    }

    /**
     * 作用：将xml转为array
     * @param $xml
     * @return mixed
     */
    public function xmlToArray($xml)
    {
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }
}